package com.hanxiaozhang.collection;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 〈一句话功能简述〉<br>
 * 〈DelayQueue使用〉
 *
 * @author hanxinghua
 * @create 2023/3/17
 * @since 1.0.0
 */
public class No3DelayQueueUse {

    public static void main(String[] args) throws InterruptedException {

        // 验证延迟队列，排序不按照剩余有效比较出现的问题。
        /*
        DelayQueue底层使用PriorityQueue队列，PriorityQueue使用compareTo()方法完成元素大小的比较。
        DelayQueue获取元素的原理：先从PriorityQueue队列弹出一个元素，调用当前元素的getDelay()获取当前剩余有效期，
        如果小于等于0，返回当前元素；如果大于0，await等待时间为剩余有效期，结束等待后，返回当前元素。
        如果compareTo()方法不使用有效期比较，队列中元素排序不准确。
        Tips: DelayQueue是线程安全的队列
         */
        DelayQueue<DelayedData1> queue1 = new DelayQueue();
        queue1.put(new DelayedData1(5L, TimeUnit.SECONDS, "数据1", 2));
        queue1.put(new DelayedData1(2L, TimeUnit.SECONDS, "数据2", 5));
        queue1.put(new DelayedData1(6L, TimeUnit.SECONDS, "数据3", 6));
        queue1.put(new DelayedData1(7L, TimeUnit.SECONDS, "数据4", 3));

        while (!queue1.isEmpty()) {
            DelayedData1 take = queue1.take();
            System.out.println(take.getData());
        }
        System.out.println("---- ---- ---- ----");

        DelayQueue<DelayedData2> queue2 = new DelayQueue();
        queue2.put(new DelayedData2(5L, TimeUnit.SECONDS, "数据1", 2));
        queue2.put(new DelayedData2(2L, TimeUnit.SECONDS, "数据2", 5));
        queue2.put(new DelayedData2(6L, TimeUnit.SECONDS, "数据3", 6));
        queue2.put(new DelayedData2(7L, TimeUnit.SECONDS, "数据4", 3));

        while (!queue2.isEmpty()) {
            DelayedData2 take = queue2.take();
            System.out.println(take.getData());
        }


    }


    /**
     * 验证延迟队列，排序不按照剩余有效比较出现的问题。
     *
     * @param <T>
     */
    public static class DelayedData1<T> implements Delayed {

        /**
         * 有效期
         */
        private Long expire;

        /**
         * 数据
         */
        private T data;

        /**
         * 比较值
         */
        private Integer compare;


        /**
         * 构造函数
         *
         * @param delayedTime
         * @param delayedTimeUnit
         * @param data
         * @param compare
         */
        public DelayedData1(Long delayedTime, TimeUnit delayedTimeUnit, T data, Integer compare) {
            this.expire = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(delayedTime, delayedTimeUnit);
            this.data = data;
            this.compare = compare;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        /**
         * 返回剩余有效期
         *
         * @param unit the time unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return expire - System.currentTimeMillis();
        }

        /**
         * 比较
         *
         * @param o the object to be compared.
         * @return
         */
        @Override
        public int compareTo(Delayed o) {
            Integer sub = this.compare - ((DelayedData1) o).compare;
            if (sub > 0) {
                return 1;
            } else if (sub == 0) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    /**
     * 验证延迟队列，排序按照剩余有效比较。
     *
     * @param <T>
     */
    public static class DelayedData2<T> implements Delayed {

        /**
         * 有效期
         */
        private Long expire;

        /**
         * 数据
         */
        private T data;

        /**
         * 比较值
         */
        private Integer compare;


        /**
         * 构造函数
         *
         * @param delayedTime
         * @param delayedTimeUnit
         * @param data
         * @param compare
         */
        public DelayedData2(Long delayedTime, TimeUnit delayedTimeUnit, T data, Integer compare) {
            this.expire = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(delayedTime, delayedTimeUnit);
            this.data = data;
            this.compare = compare;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        /**
         * 返回剩余有效期
         *
         * @param unit the time unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return expire - System.currentTimeMillis();
        }

        /**
         * 比较
         *
         * @param o the object to be compared.
         * @return
         */
        @Override
        public int compareTo(Delayed o) {
            Long sub = this.expire - ((DelayedData2) o).expire;
            if (sub > 0) {
                return 1;
            } else if (sub == 0) {
                return 0;
            } else {
                return -1;
            }
        }
    }

}


